\chapter{Job Ads}
\label{chap:jobadsapp}

Suppose that our company web site will soon feature a new set of
job ads\index{job ads example}.
The old way of typing directly into HTML pages has worn through.  In this
chapter, we'll build the foundation for the new system, by creating a web
app to enter and update job and position descriptions.

The key features of the application are:
many-to-many relationships between database tables,
creative CRUD where a single page submission
leads to activity in multiple tables, and
limiting who can access the app (auth).

\section{Step One: Creating Jobs and Their Skills}

Eventually the powers that be want a complete on-line way to manage job
descriptions, position announcements, and to track applications for those
positions.

While the plans for the app are ambitious, we are starting modestly,
with two main concepts: job descriptions and skills.  Both of these will
be a table in the database, but we need one more.  Since the relationship
between skills and job descriptions is many-to-many, we need a table to
join them.

A job description will have a title and a general description, like
``Cleaner'' - ``Keeps the building sparkling.'' Skills are things that
people are able to do, like ``coffee making.''  But we will probably
stretch the term skill to include things like ``valid driver's license
and clean driving record.''  Typically, many skills are required for
each job, but many jobs may also share a particular skill.

The next step would be to have position announcements, so we can invite
highly motivated self-starters to join our team-oriented environment.
Then, we could add a way to track the candidates who apply, etc.  For
now, we will start small.

We're ready to write the vast bulk of the two table app.  Let's start with
tentmaker\index{tentmaker!many-to-many}:

{\footnotesize
\begin{verbatim}
tentmaker -n JobAd 'job<->skill'
\end{verbatim}
}

As in other examples, I supplied table names separated by a relationship
operator.  This one is for
many-to-many\index{many-to-many}\index{tables!many-to-many}.
See Table \ref{tab:asciiart} in Section \ref{sec:asciiart}
for a list of the others.

With the structure in place, use tentmaker to make the normal changes.
To begin, change the name of the ident field in the job table to `title.'
For once, `description' can stay as is.  Then, add fields for
travel percent, on site, and time percent.

The last two new fields will require the user to choose from a list of
options.  In the last chapter, we had a similar problem with the invoice
status.  There, I chose to use a table to specify the statuses.  We
could do the same here.  But, in the interest of diverse solutions and to
save data model complexity, let's let the app front end control the choices.

Start by choosing \verb+on_site+ from the `Edit Field:' selection
list.  That will reveal form elements which control all aspects of the
field and should look something like Figure \ref{fig:fieldedit1}.

\begin{figure}
\begin{center}
\includegraphics[width=4in]{fieldedit}
\end{center}
\caption{Full field edit form (same as Figure \ref{fig:fieldedit2}).}
\label{fig:fieldedit1}
\end{figure}

Some of these are represented in the quick edit table, but not
the ones we need to set.  First, choose `select' from the
\verb+html_form_type+\index{html form type@\verb+html_form_type+}
drop down menu.  Then, scroll down to
\verb+html_form_options+\index{html form options@\verb+html_form_options+}.
Enter the values from Table \ref{tab:onsitevals}.

\begin{table}
\begin{center}
\begin{tabular}{l|l}
Label               & Database Value    \\
\hline
On-Site             & onsite            \\
Partial Telecommute & parttele          \\
Telecommute         & tele              \\
\end{tabular}
\end{center}
\caption{Values for job description on site column.}
\label{tab:onsitevals}
\end{table}

Do the same things for \verb+time_percent+\index{time
percent@\verb+time_percent+}.
First, choose select as the
\verb+html_form_type+.  Then, enter options like those in Table
\ref{tab:timepercentvals}.

\begin{table}
\begin{center}
\begin{tabular}{l|l}
Label               & Database Value \\
\hline
Full Time           & full           \\
Part Time           & part           \\
Contract            & con            \\
\end{tabular}
\end{center}
\caption{Values for job description on time percent column.}
\label{tab:timepercentvals}
\end{table}

This technique gives us data integrity, so long as all database access
is through the app.  But it saves us two tables in this case.

That's all we need to do for the job table, but we need to change the
type of its controller from AutoCRUD to CRUD\index{CRUD}.
We must do this because the
entry form for a job will allow the user to select the job's required skills.
Those skills not only come from another table, there can be many of them.
As we will see below, making the controller type CRUD before initial generation
will generate a lot of useful code, leading us a long way down the path
to enormous CRUD flexibility.

We must also change the type\index{method!type CRUD form}\index{CRUD!forms}
of the form method in the Job controller
to \verb+CRUD_form+.  I also like to change the name of the form method
to something more colorful, like
\verb+job_form+\index{job form@\verb+job_form+}.
This name is the
basis for quite a bit of generated code, so something descriptive is
definitely in order.  That's finally it for jobs.

The skill table is the rare bird that can use the default fields.
We have done enough to put in the skills and job descriptions.
But we still don't have a way to link the skills to their
job descriptions -- we'll use the flexibility of Gantry's CRUD plugin
to handle that.  Further, as we'll see later, we haven't met management's
full requirement set.  We do have our starting point, so save the
bigtop file and stop tentmaker.

Since we didn't start with bigtop in new mode, we need to do the initial
build of the
app\index{bigtop!initial build from source}\index{bigtop!--create or -c}:

{\footnotesize
\begin{verbatim}
bigtop -c jobad.bigtop all
\end{verbatim}
}

This creates the build directory, \verb+JobAd+, which you should change to.
You could spell out \verb+-c+ as \verb+--create+.  We are ready to begin
customizing
the application.  First, we will deal with the linkage of skills to jobs.
Then we will show it to users and deal with their inevitable objections.

\section{Many-to-Many\index{many-to-many} Relationships}
\label{sec:many2many}

Immediately after bigtop generation, you can create your database.  I made
mine with SQLite:

{\footnotesize
\begin{verbatim}
sqlite app.db < docs/schema.sqlite
\end{verbatim}
}
\noindent
Then you can test drive the app.  The only thing you can't do is
join skills to job descriptions.  We'll have to write some code for
that.

Our chief new requirement is a way to join\index{join table}
skills to jobs.  We have
several options for implementing this.  Here's
what I did.  I modified the job controller so that when creating a job
you can pick the skills that go with it\footnote{Note that I am purposely
ignoring
the normal distinction between required and desired skills, feel free
to improve the app to handle those, if you want to actually use it.}.
This requires me to specify the skills first, so they are available
during job description creation.  That seems like a fairly obvious
work flow.  But if you preferred some other flow, you could accommodate
it with similar techniques.

Since I told tentmaker that we wanted a three way
join between job and skill (through the implicit
\verb+job_skill+\index{job skill table}
table),
the model can already handle the many-to-many relationship.  But we
need a way to allow the user to create and manage the joining rows.

\subsection{Gantry's CRUD plugin}

Before we get down to the actual work, we need to learn a bit about the
CRUD plugin.  It is discussed again in greater detail in Chapter
\ref{chap:fullcrud}.  Here we will cover just enough ground to finish
our case study.

To this point we have relied on AutoCRUD, which is enough
for most circumstances.  But, in this case we need more.  The
CRUD plugin\index{CRUD!plugin}
is not really a plugin at all, since it doesn't provide mixin methods
like \verb+do_add+.  Instead, it provides an object oriented interface
to add, edit and delete work flows.

When you set the type of the Job controller to CRUD, bigtop made a lot
of methods, and some other code, in the stub\footnote{providing you set
the type
before initial generation, since bigtop puts nothing into the stub after
that}.  Let's see what it made.
First, it made a crud object:

{\footnotesize
\begin{verbatim}
my $job = Gantry::Plugins::CRUD->new(
    add_action      => \&job_add,
    edit_action     => \&job_edit,
    delete_action   => \&job_delete,
    form            => __PACKAGE__->can( 'job_form' ),
    text_descr      => 'job',
);
\end{verbatim}
}

I deleted the redirect key from the generated constructor arguments.  It allows
you to completely control where users go after they cancel, or
successfully save, the form.  In our case the default -- which goes back
to \verb+do_main+ -- is fine.

The \verb+$job+\index{\$job@\verb+$job+}
object is now ready to display our form, validate it,
and call back the actions when we should save the result.  The actions
and the form are shown below.

For this to work, we need to implement \verb+do_add+, \verb+do_edit+,
and \verb+do_delete+ for ourselves.  But, bigtop makes those for us.
If we don't have anything fancy to do, the generated ones will work.
To have an example, consider the default
\verb+do_add+\index{do add@\verb+do_add+}:

{\footnotesize
\begin{verbatim}
sub do_add {
    my $self = shift;

    $job->add( $self, { data => \@_ } );
}
\end{verbatim}
}

You don't really need to pass \verb+\@_+, but it can't hurt.  It won't
be useful unless there are additional parameters on the end of the URL.
The other \verb+do_*+ methods are similar and will work out of the box,
if you use DBIx::Class.

Let's think for a second (or several) about the
work flow\index{CRUD!work flow}.  When the
user clicks `Add' on the main listing page, they come to \verb+do_add+.
It calls \verb+add+ on the \verb+$job+ CRUD object.  The CRUD plugin
then calls back to our \verb+job_form+ method (see below), which
it uses to populate the form template.  That request finishes with the
display of the form page.

When the user clicks `Save,' the request comes back through our \verb+do_add+,
which again calls \verb+add+ on the CRUD object.  If the form validates,
it calls back to our \verb+job_add+ method.  All of the call backs
are specified in the constructor call for the \verb+$job+ CRUD object.
After that call back finishes updating the database, the CRUD plugin
redirects the user back to the main listing.\footnote{We could control that
step with a redirect call back, but the default is usually effective.}

\subsection{Customizing CRUD}

Now that you have an idea of how requests flow through our code and
the CRUD object, we are ready to specialize the call backs to our problem.
Consider adding jobs first.  After \verb+do_add+ calls \verb+add+ on
the CRUD object, the next thing that happens -- from our perspective --
is a call back to our \verb+job_form+ method.

The generated default is pretty useful, but we do need to get the
skills onto the form.  There are two parts to that: find all available
skills which could be associated with any job, and find all the skills
already associated with the current job.  The later step only applies
to edit, but I'll show it anyway, since it lives in the form method
shared by add and edit.

The \verb+job_form+\index{job form@\verb+job_form+!code} method starts
likes this:

{\footnotesize
\begin{verbatim}
 1 sub job_form {
 2     my ( $self, $data ) = @_;
 3 
 4     # skills (three way join with skill table via job_skill)
 5     my @skill_options;
 6     my @all_skills = $SKILL->gsearch(
 7             $self, undef, { order_by => 'ident' }
 8     );
 9 
10     foreach my $skill ( @all_skills ) {
11         push @skill_options, {
12             value => $skill->id,
13             label => $skill->ident,
14         };
15     }
\end{verbatim}
}

To get the list of all skills, we can call (lines 6-8) the Gantry sugar
method, \verb+gsearch+\index{gsearch@\verb+gsearch+ ORM sugar
method}\index{ORM!sugar!gsearch@\verb+gsearch+},
then walk the returned list building an array of choice
hashes (lines 10-15).  These will be displayed in a multiple select
list in order by ident.

Finding the skills during edit involves using the
many-to-many\index{many-to-many} relationship
built for us by bigtop.  First, we need a hash, called
\verb+%selected_skill+,\index{selected skill@\verb+%selected_skill+}
keyed by skill id.  The value will be one, if the
skill is selected.  Then we need to get all the skills and stuff them
into the hash:

{\footnotesize
\begin{verbatim}
16     my %selected_skill;
17 
18     if ( defined $data->{ row } ) {
19         my @skills = $data->{ row }->skills;
20 
21         foreach my $skill ( @skills ) {
22             $selected_skill{ $skill->id }++;
23         }
24     }
25 
\end{verbatim}
}

Note that we can look in the data passed to the \verb+job_form+ method
for the row, but only because our \verb+do_edit+ method puts it there.
It is futile to look for skills if we are adding the job, since there aren't
yet skills in that case.  Once we know that we
are editing, we can call \verb+skills+ on the row object (line 19) to get
an array of skills belonging to the current row.  A simple foreach stuffs them
into the hash (lines 21-23).

Whenever we run bigtop, it regenerates the form hash in the GEN module.
We only need to add the skill\index{skill form field} field to it.  This
is easiest with the form munger:

{\footnotesize
\begin{verbatim}
26     # now pull the generated form and add the skills to it
27     my $form = $self->SUPER::job_form( $data );
28 
29     my $munger = Gantry::Utils::FormMunger->new( $form );
30 
31     $munger->append_field(
32         {
33             name     => 'skill',
34             type     => 'select_multiple',
35             label    => 'Skills',
36             options  => \@skill_options,
37             selected => select_multiple_closure(
38                     'skill',
39                     \%selected_skill,
40             ),
41         },
42     );
43 
44     return $form;
45 }
\end{verbatim}
}

The documentation at the top of Gantry's
\verb+form.tt+\index{form.tt@\texttt{form.tt}} explains what
is legal in the form hashes.  In particular, our skill field needs name,
type, label, options, and selected keys.  The first three are probably
obvious.  The name is the name of the form input element.  Usually this
is the same as a table column name in the database.  For multi-selects
it is arbitrary.  The options array was built above.  The
\verb+select_multiple_closure+\index{select multiple
closure@\verb+select_multiple_closure+}
function is exported by the CRUD plugin,
but not by default.  To use it you need to ask for it.  In fact, we need
to make several changes at the top of \verb+Job.pm+ to make all of this
work:

{\footnotesize
\begin{verbatim}
use Gantry::Plugins::CRUD qw( select_multiple_closure );
use Gantry::Utils::FormMunger;
use JobAd::Model::skill     qw( $SKILL );
use JobAd::Model::job_skill qw( $JOB_SKILL );
\end{verbatim}
}

The \verb+select_multiple_closure+ function returns a closure the template
uses to light up the previously saved skills.  You must pass it the name of
the field and the hash of selected items.  It takes care of deciding whether
the user has modified the form, and of returning true or false for each
possible skill.

With the form complete, we can return to the activity call backs.  I'll
start with
\verb+job_add+\index{job add
callback@\verb+job_add+ callback}\index{CRUD!callbacks!job add@\verb+job_add+},
which is called when the user is attempting
to save a new job, but only after the form has validated.  It receives
the hash of parameters from the form (less the submit key), and whatever
data you passed to \verb+add+ on the CRUD object.

We need to remove the skill key, recover the individual skills, then
create the job and job to skill rows.  The database work should
be done in one transaction.

{\footnotesize
\begin{verbatim}
sub job_add {
    my ( $self, $params, $data ) = @_;

    # first pull skills out of params
    my $r_new_skills = delete $params->{ skill };
    my @skills = ( ref $r_new_skills eq 'ARRAY' )
               ? @{ $r_new_skills }
               : ( $r_new_skills );

    my $schema = $self->get_schema();

    $schema->txn_begin();

        # add the job row
        my $new_job = $JOB->gcreate( $self, $params );

        # add the job_skill rows
        foreach my $skill ( @skills ) {
            $JOB_SKILL->gcreate( $self,
                {
                    job   => $new_job->id,
                    skill => $skill,
                }
            );
        }

    $schema->txn_commit();
}
\end{verbatim}
}

Note that when a form parameter can have
multiple values\index{forms!HTML select multiple elements},
we receive either a scalar (if there is only one) or an array reference
(if there are several).  Note that this is no longer the same behavior
exhibited by CGI.pm as of Gantry 3.51.  Creating the row is simple with the
\verb+gcreate+\index{gcreate@\verb+gcreate+ ORM sugar
method}\index{ORM!sugar!gcreate@\verb+gcreate+}
sugar method.
I used it again to make the job to skill rows.

Deleting is mostly the same process in reverse:

{\footnotesize
\begin{verbatim}
sub job_delete {
    my ( $self, $data ) = @_;

    my $schema     = $self->get_schema();
    my $doomed_row = $JOB->gfind( $self, $data->{ row }->id );

    $schema->txn_begin();

        $JOB_SKILL->gsearch(
                $self, { job => $doomed_row->id }
        )->delete_all;

        $doomed_row->delete();

    $schema->txn_commit();
}
\end{verbatim}
}

First, we need to delete\index{delete!job descriptions}
the job to skill joining rows, then remove the
job itself.
Finally, we arrive at the most complicated piece:
editing\index{editing job descriptions}.  Here we need
to update the data in the job row, but also correct the job to skill rows.
I'll take the approach of deleting the deselected rows and creating rows
for new selections, while leaving the others unchanged\index{job edit}.

{\footnotesize
\begin{verbatim}
 1 sub job_edit {
 2     my( $self, $params, $data ) = @_;
 3 
 4     my $row = $data->{ row };
 5 
 6     my @new_skills;
 7     if ( defined $params->{ skill } ) {
 8         my $r_new_skills = delete $params->{ skill };
 9         @new_skills = ( ref $r_new_skills eq 'ARRAY' )
10                    ? @{ $r_new_skills }
11                    : ( $r_new_skills );
12 
13     }
14     my %new_skills = map { $_ => 1 } @new_skills;
15 
16     my $schema = $self->get_schema();
17 
18     $schema->txn_begin();
19 
20         # remove deleted skills
21         my @old_skills = $row->job_skills;
22 
23         foreach my $old_skill ( @old_skills ) {
24 
25             my $skill_id = $old_skill->skill->id;
26 
27             if ( $new_skills{ $skill_id } ) {
28 
29                 delete $new_skills{ $skill_id };
30 
31             }
32             else {
33                 $old_skill->delete;
34             }
35         }
36 
37         # create new skills
38         foreach my $new_skill_id ( keys %new_skills ) {
39             $JOB_SKILL->gcreate(
40                 $self,
41                 {
42                     job   => $row->id,
43                     skill => $new_skill_id,
44                 },
45             );
46         }
47 
48         # update job row
49         $row->update( $params );
50 
51     $schema->txn_commit();
52 }
\end{verbatim}
}

For ease of typing, I named \verb+$row+ for the current row object on line 4.
As in add, we need to handle the \verb+skills+ parameter to form
a list of the skills (lines 8-11).  We must remove it from the hash
(line 8), since it is not a column in the job table.  Failing to do that
will lead to a fatal error when we call update with the parameter hash on
the current row (line 49).

Even though I don't usually like map, this looked like a good place for
it (line 14).
I need to convert the list of skills from the form into a lookup hash:
\verb+%new_skills+.\index{new skills@\verb+%new_skills+}
It now has an entry for each currently selected
skill.  These are the only skills which should be joined to this job
when we are done.

Getting the job to skill rows is as simple as calling \verb+job_skills+
on the current row (line 21).  Looping through them (lines 23-35), I see
if the skill is in \verb+%new_skills+, meaning it is still selected (line 27).
If it is, I remove it from \verb+%new_skills+, so it won't be duplicated
later (line 29).  Otherwise,
I use \verb+delete+\index{delete!ORM method}
to remove it from the database (line 33).  Once this process
is finished, only genuinely new skills are in the hash.  So, we
can walk its keys creating new rows (lines 38-46).
After the skills are updated, all we have to do is update the job row
itself (line 49) and commit the database transaction (line 51).

There are still many features I'm leaving out of this study.  Chief among them
position announcements.  Feel free to test drive the app.  Create some
skills and a job or two.  But, don't get to attached to the data just yet.
We have a functional app and it is time to turn to authenticating users.

\section{Authorizing Users\index{auth}}
\label{sec:authusers}

Upon seeing the app from the last section, management
immediately asked us to track logged in users and what they did to the data.
This is a two fold request.  First, we need a login scheme, so only certain
people can access the app.  Second, when they are in the app, we need to
track what they do in a change log.  This section tackles the first
request, the next section the second.

There are many ways to authorize users in a web app.  Gantry can help with
two of those approaches.  Originally, Gantry provided only Apache basic
auth\index{auth!Apache basic}, which is not enough for CGI users.  Since
then, we have
added\footnote{we as in Tim Keefer} a cookie based
scheme\index{auth!cookie based}.  This allows
anyone to use auth.  It also frees you from our auth data model which is
required for the basic auth approach.

Below, I will describe the cookie based auth scheme.  If you are interested
in the basic auth scheme, start by reading with
Gantry::Control::C::AuthenRegular.
Note that you will need to implement
an auth database (which could be inside your app database, but doesn't need
to be).  The schema for that database is in the docs subdirectory of the
Gantry distribution as authschema.sql (for PostgreSQL) and authschema.sqlite
(for SQLite).

Note that whether you use Apache basic auth or cookie based auth, you should
run your server over SSL, keep it behind a firewall, or both.

Gantry's cookie based auth can use a file in the same format as an
Apache htpasswd\index{htpassword}
file.  But, here I will show a database approach.  This
will allow us to control the users through the web app just as we
manage any other set of tables.

\subsection{Auth Data Model\index{auth!cookie based!data model}}

First, we need to add three tables to our database.  The first one
provides a row for each legal user.  The other two list the valid
groups and their members.  Here's what we need to do as we start
tentmaker\index{tentmaker!augmenting}\index{tentmaker!adding auth}:

{\footnotesize
\begin{verbatim}
tentmaker -a docs/jobad.bigtop 'user<->grouping'
\end{verbatim}
}

\noindent
I would prefer the table name `group,' but that is reserved in SQL.

Then we need to make sure that the new user table has user name and
plain text password fields.  To take the defaults in the Auth Cookie
plugin\index{auth!plugin defaults},
name the fields \verb+ident+ and \verb+password+.  Otherwise,
you will need to supply conf variables with the names of the
fields.\begin{footnote}The conf variables are \verb+auth_user_field+
and \verb+auth_password_field+.\end{footnote}

Be sure to include any other data about the
user that you want to use in other parts of the app.  In our case, we
want at least the user's actual name.  But, we might also want their
email address and/or phone extension.  We'll see how to retrieve those
things inside our app below.

The group table needs to have an \verb+ident+ column, the rest is up
to you.
The join table needs to have names\index{join table!names}.
The name on the group side (the
one under group) needs to be \verb+groups+.  The name of the other
side is up to you\footnote{I chose users}, but you must
include one explicitly.

Save the result, and stop tentmaker.  But, before rebuilding, edit the
Bigtop file and add some \verb+data+
statements\index{data statements}\index{table!data statements}
to the user and group tables:

{\footnotesize
\begin{verbatim}
    table user {
        #...
        data
            username   => pcrow,
            first_name => Phil,
            last_name  => Crow,
            password   => test;
        data
            username   => lcrow,
            first_name => Lisa,
            last_name  => Crow,
            password   => test;
    }
    table grouping {
        #...
        data
            ident => HR,
            description => `Human Resources Staff`;
    }
    join_table user_grouping {
        #...
        data
            user => 1,
            grouping => 1;
    }
\end{verbatim}
}

Now, rebuild the application with bigtop:

{\footnotesize
\begin{verbatim}
bigtop docs/jobadd.bigtop all
\end{verbatim}
}

This requires altering the existing database.  Since I never put any real
data into mine, I could just discard the original and recreate it.
Alternatively, I could cut and paste the new tables from
\verb+docs/schema.sqlite+ into the sqlite command line tool, since all
the changes involve making new tables.
With the database in place, we are ready to activate cookie based
authentication.

\subsection{Cookie Based Auth}

The first step to Gantry's cookie auth
scheme\index{auth!cookie based}
is to use it.
We need to do this in at least two places: wherever the app is used
with \verb+-Engine+ add to the import list so it looks like this:
{\footnotesize
\begin{verbatim}
    use JobAds qw{
        -Engine=MP13
        -TemplateEngine TT
        -PluginNamespace=JobAds
        AuthCookie
    };
\end{verbatim}
}
\noindent
See Chapter \ref{chap:plugins} for an explaination of namespaces.
Also use the plugin in the base module,\footnote{or in the module which is
at the top of the authed hierarchy}
so it can import \verb+do_login+ from
the plugin:

{\footnotesize
\begin{verbatim}
use Gantry::Plugins::AuthCookie;
\end{verbatim}
}
\noindent
In addition, we need to bring in the model plugin like this:

{\footnotesize
\begin{verbatim}
use JobAd::Model;

use Gantry::Plugins::DBIxClassConn qw( get_schema );

sub schema_base_class { return 'JobAd::Model'; }
\end{verbatim}
}
\noindent
Then, in the init method (if you don't have one, make a new one), include
calls to \verb+auth_require+\index{auth require@\verb+auth_require+}
and \verb+auth_deny+\index{auth deny@\verb+auth_deny+}:

{\footnotesize
\begin{verbatim}
sub init {
    my ( $self, $r ) = @_;

    $self->SUPER::init( $r );
    $self->auth_require( 'valid-user' );
    $self->auth_deny( 'yes' );
}
\end{verbatim}
}

This sets the valid user requirement for all pages in this controller
and for all other controllers which inherit from
it.\begin{footnote}though they could set the
flag to `no' in their own \verb+init+ methods to cancel the
effect\end{footnote}
Note that I've done this unconditionally,
but you could test \verb+$self->uri+, or other things in the Gantry site
object, and set the flag only for certain conditions.  Immediately below,
we will see another value for \verb+auth_require+.

You probably also want to set a value for the cryptographic key, by calling
\verb+auth_secret+.
Otherwise, you'll get the default which is on CPAN and thus hardly secure.
As with \verb+auth_require+ and \verb+auth_deny+, you can set it via
conf as discussed in the next section.

%All that remains is to supply the conf variables that tell the plugin
%how to perform the authentication.  We need to add these to our conf
%(do this with tentmaker or a text editor on \verb+docs/jobad.bigtop+,
%then regenerate):
%
%{\footnotesize
%\begin{verbatim}
%    conf {
%        auth_table          user         => no_accessor;
%        auth_user_field     username     => no_accessor;
%        auth_password_field passwd       => no_accessor;
%        auth_require        `valid-user` => no_accessor;
%    }
%\end{verbatim}
%}
%
%Note that the accessors for all of these variables are provided by the
%AuthCookie plugin.  Also note that this is where you must correctly spell
%the names of the user table and its columns, so they match the new
%tables you made in the database.

The only thing that remains is to add a slight init variation to the
controllers which should be restricted to the HR
group\index{auth!group based}
only.  For them,
use this init:

{\footnotesize
\begin{verbatim}
sub init {
    my ( self, $r ) = @_;

    $self->SUPER::init( $r );

    $self->auth_require( 'group' );
    $self->auth_groups( 'HR' );
}
\end{verbatim}
}

That is all there is to turning on the cookie based auth scheme
programmatically.  The next subsection shows how to accomplish the same
thing with configuration.

\subsection{Conf Control of Auth\index{auth!controlling via conf}}

We could set the \verb+auth_deny+\index{auth deny@\verb+auth_deny+}
and \verb+auth_groups+\index{auth groups@\verb+auth_groups+} flags
in conf instead of in \verb+init+ methods.  This is natural for
\verb+mod_perl+ where you can do it in the same manner as you would for
basic auth.  At each location, you can include \verb+PerlSetVar+s.
This will not work for CGI, since the conf for Gantry CGI objects
is all at one level.  While, that would allow you to require valid
users or groups, you could only do it for the whole app or none of it.
Further, you couldn't change which groups are required on a per page basis.

Gantry::Conf\index{Gantry::Conf!controlling auth with}
solves this.  It allows you to specify conf info per location
regardless of your engine choice.
All you have to do to make it work is choose the Conf
Gantry\index{bigtop!Conf Gantry backend}\index{Conf Gantry bigtop backend}
backend in tentmaker, supply your instance name, then tell the HttpdConf
Gantry\index{bigtop!HttpdConf Gantry backend!with Conf Gantry}
or the CGI
Gantry\index{bigtop!CGI Gantry backend!with Conf Gantry}
backend that you are using it, by checking the `Use Gantry::Conf' box.
Finally, you must include location level config blocks, which tentmaker
cannot help you do, so employ your favorite text editor.  All that is
needed is a config block inside each
relevant controller.  For our app, that looks something like this:

{\footnotesize
\begin{verbatim}
app JobAdd {
    config {
        #...
        auth_deny yes;
        auth_require `valid-user`;
    }
    #...
    controller Skill {
        config {
            auth_require group;
            auth_groups HR;
        }
        #...
    }
}
\end{verbatim}
}

The config
block\index{config block!controller level}\index{controller!config block}
for Skill should also go in all of the other controllers
which need to be limited to the HR group.

%To understand Gantry's auth scheme, you need to understand a bit about
%how our business uses Gantry.  We deliver many applications via the
%web.  Most of them are served to employees behind our firewall.  Therefore,
%we want all the apps to use the same user names and passwords.  In fact,
%we only want to store those once.  So, we have a master auth database
%where everyone's login credentials are stored.  Ideally, each app uses
%it for validation.  In reality there are exceptions, with some apps
%periodically pulling updates from master auth into their own auth tables.
%Here, I will focus on the ideal.  Thus, there will be two databases:
%jobad.db and auth.db.  The second can easily be shared across an
%organization's apps (we even share ours with apps written with the
%prior version of Gantry, which never left the shop).  I must admit that
%this approach also simplifies the following discussion.  It is harder to
%use Gantry's auth scheme when the auth tables are embedded in the app's
%database, not impossible, just harder.
%
%To create your auth database, use a schema file that comes in the
%gantry distribution: docs/authschema.sql (PostgreSQL) or
%docs/authschema.sqlite.
%As we will see below, Gantry provides controllers for maintaining the
%data in the database once you build it.
%
%Here is a tour of the auth data model.  The key table is \verb+auth_users+.
%It has the user name, real name, and password for each user.  The other
%tables are tied to it in one way or another.
%
%In addition to individual rights, many apps, including this one, have group
%requirements.  Suppose that any valid user may update or add job descriptions
%and skills, but only HR can create and manage positions.  This leads to one
%group: HR.  In addition, we might want an admin group to manage the
%\verb+auth_users+ table.  For now the HR group members will be allowed to
%do that.  Gantry auth uses \verb+auth_groups+ to name the groups and
%\verb+auth_group_members+ to enroll users in them.  There is also an IP based
%auth scheme, but we don't need to think about that for this app.
%
%We don't need to add tables or controllers to our bigtop file for the
%auth scheme, but we do need to add some conf to make it happen.  First,
%I'll use \verb+mod_perl+.  You could also use CGI, but not the stand alone
%server, which does not understand the HTTP auth protocol.
%
%
%
%
%
%This is enough to put in the job descriptions, the position announcements,
%and the skills.  But it doesn't provide a way to link the skills to their
%job descriptions.  And, as we're about to see, it doesn't meet management's
%full requirement set.
%
%This bigtop file assumes that you are going to incorporate the auth tables
%into your app's regular database.  But, you could change that simply by
%altering the DBI connection string for \verb+auth_dbconn+ in the app level
%config block.
%
%The best way to set up the auth part of your database is to use the schema
%provided in the docs directory of the Gantry distribution, though that will
%take a bit of translation if you don't use PostgreSQL.
%
%Once you have the database set up, you can easily turn on location based
%auth by user or group in your apache conf.

\section{Tracking User Changes}
\label{sec:jobadchangelog}

Now that users must log in, we know who they are.  So, we can track their
changes.  Let's start by adding a table for
changes\index{change log, example}\index{tentmaker!augmenting}:

{\footnotesize
\begin{verbatim}
tentmaker -a docs/jobadd.bigtop 'change_log->user'
\end{verbatim}
}

This will create a controller we don't need, so delete \verb+ChangeLog+,
but keep the table.  After saving, rebuild:

{\footnotesize
\begin{verbatim}
bigtop docs/jobadd.bigtop all
\end{verbatim}
}

Add the \verb+change_log+ table from \verb+docs/schema.sqlite+ to
the database.  All that remains is to make the notes.  Since we are
using both AutoCRUD and CRUD, we will do this in two different places.
I'll do this manually in both places,
then show a more sensible approach.

The skill table is controlled with AutoCRUD, so we need to use hooks to
update the change log.  Note that AutoCRUD always autocommits.  This could
create problems, even in our little app.  If it does, or the idea of the
problems overwhelms you, feel free to always use CRUD.  Just be prepared
to do a bit more work.

After AutoCRUD performs database actions, it calls back to
hooks\index{AutoCRUD!callback hooks}
(if they exist).  The two we care about are
\verb+add_post_action+\index{add post action@\verb+add_post_action+} and
\verb+edit_post_action+\index{edit post action@\verb+edit_post_action+}.
To make a change log entry after a new skill
is added, we can implement this:

{\footnotesize
\begin{verbatim}
sub add_post_action {
    my ( $self, $new_row ) = @_;

    $CHANGE_LOG->gcreate(
        $self,
        {
            ident   => 'Added skill ' . $new_row->ident,
            user    => $self->auth_user_row->id,
            created => \'datetime("now")',
        }
    );

    my $schema   = $self->get_schema()->txn_commit();

}
\end{verbatim}
}

Note that the created time value is only good for SQLite.  If you are
using MySQL, you want \verb+\'NOW()'+.  For PostgreSQL, you can just
use \verb+'now'+.  The leading backslash takes a reference to the scalar
so that it is evaluated at the proper time by DBIx::Class.

CRUD users can do a more obvious thing.  They are already in full
control of all database work, so the change log entry is just one
more thing to do during row creation or updates.  For example, we
can add the following immediately before the transaction commit
in our existing \verb+job_edit+\index{job edit@\verb+job_edit+!logging changes} method:

{\footnotesize
\begin{verbatim}
    $CHANGE_LOG->gcreate(
        $self,
        {
            ident   => 'Added skill ' . $new_row->ident,
            user    => $self->auth_user_row->id,
            created => \'datetime("now")',
        }
    );
\end{verbatim}
}

This, of course, looks strikingly similar to the code that went into
the add post action in the Skill controller.  It should probably be
factored out into a method in the base controller\index{log change}:

{\footnotesize
\begin{verbatim}
sub log_change {
    my ( $self, $message ) = @_;

    $CHANGE_LOG->gcreate(
        $self,
        {
            ident   => $message,
            user    => $self->auth_user_row->id,
            created => \'datetime("now")',
        }
    );
}
\end{verbatim}
}

This relies on the caller to perform the commit, but that makes sense,
since the logging of change is often part of other transactions.

No matter where you locate the change log code, remember to add a use
statement for the change log model to the module:

{\footnotesize
\begin{verbatim}
use JobAd::Model::change_log qw( $CHANGE_LOG );
\end{verbatim}
}

\section*{Summary}

In this chapter we have learned to implement many-to-many joins,
to authenticate users with Gantry's cookie based scheme, and to log
change based on user credentials.  This is our last case study.

The remaining sections of this book go into greater detail, so this is
the best point to begin hopping to things which interest you.

